'use strict';

const path = require('path');

const Conf = require('conf');
const inquirer = require('inquirer');
const fsExtra = require('fs-extra');

const Command = require('@x3-cli-dev/command');
const log = require('@x3-cli-dev/log');
const { getProjectList, getPluginList } = require('@x3-cli-dev/request');
const { execCommandAsync } = require('@x3-cli-dev/utils');

const { CONF_SCHEMA } = require('./schema');

class InstallCommand extends Command {
  init() {
    this.source = this._argv[0];
    this.destination = this._argv[1];
    this.skipBuild = !!this._options.skipBuild;
    this.skipPack = !!this._options.skipPack;

    log.verbose('source', this.source);
    log.verbose('destination', this.destination);
    log.verbose('skipBuild', this.skipBuild);
    log.verbose('skipPack', this.skipPack);
  }

  async exec() {
    try {
      await this.prepare();
      if (!this.skipBuild) {
        await this.buildPackage();
      }
      if (!this.skipPack) {
        await this.packPackage();
      }
      await this.movePackage();
      await this.installPackage();

    } catch (error) {
      log.error(error.message);
      if (process.env.LOG_LEVEL === 'verbose') {
        console.log(error);
      }
    } finally {
      process.exit(0);
    }
  }

  async prepare() {
    await this.checkProjectAndPlugin();
    await this.checkConf();
    await this.checkPath();
  }

  /**
   * 检查线上配置
   */
  async checkProjectAndPlugin() {
    const projects = await getProjectList();
    const plugins = await getPluginList();

    const plugin = plugins.find(item => item.packageName === this.source);
    if (!plugin) {
      log.verbose('plugin', plugin);
      throw new Error(`无法找到 ${this.source} 插件, 请检查 plugin json 配置`);
    }
    const project = projects.find(item => item.packageName === this.destination);
    if (!project) {
      log.verbose('projects', projects);
      throw new Error(`无法找到 ${this.destination} 项目, 请检查 project 配置`);
    }
    this.projectInfo = project;
    this.pluginInfo = plugin;
    log.verbose('projectInfo', this.projectInfo);
    log.verbose('pluginInfo', this.pluginInfo);
  }

  /**
   * 检查路径
   */
  async checkPath() {
    const pluginPath = path.join(this.repoRootPath, this.pluginInfo.repoPath);
    const pluginFullOutPath = path.join(pluginPath, this.pluginInfo.outputPath);

    const projectPath = path.join(this.repoRootPath, this.projectInfo.repoPath);
    const projectLibPath = path.join(projectPath, 'libs');

    log.verbose('pluginPath', pluginPath);
    log.verbose('pluginFullOutPath', pluginFullOutPath);
    log.verbose('projectPath', projectPath);
    log.verbose('projectLibPath', projectLibPath);

    if (!fsExtra.pathExistsSync(pluginPath)) {
      throw new Error(`找不到 plugin 项目, 请检查路径 ${pluginPath} 或者线上 plugin 配置`);
    }
    if (!fsExtra.pathExistsSync(projectPath)) {
      throw new Error(`找不到 project 项目, 请检查路径 ${projectPath} 或者线上 project 配置`);
    }
    this.pluginInfo.pluginPath = pluginPath;
    this.pluginInfo.pluginFullOutPath = pluginFullOutPath;

    const pkgFile = path.join(pluginPath, `projects/${this.source}/package.json`);
    const pkg = require(pkgFile);
    this.pluginInfo.packFileName = `${pkg.name}-${pkg.version}.tgz`;
    log.verbose('packFileName', this.pluginInfo.packFileName);

    this.projectInfo.projectPath = projectPath;
    this.projectInfo.projectLibPath = projectLibPath;
  }

  async checkConf() {
    const cliPath = process.env.X3_CLI_HOME_PATH;
    const config = new Conf({
      cwd: cliPath,
    });
    const hasConfObj = config.get();
    const needSet = CONF_SCHEMA.filter(item => hasConfObj[item.key] === undefined);
    const confObj = await inquirer.prompt(
      needSet.map(item => ({
        name: item.key,
        message: item.message,
        type: item.type,
        default: item.default,
      })),
    );
    for (const key of Object.keys(confObj)) {
      config.set(key, confObj[key]);
    }
    log.verbose('config', config.get());
    this.confStore = config;
    this.repoRootPath = this.confStore.get('repoRootPath');
  }

  async buildPackage() {
    const commands = this.pluginInfo.buildCommand;
    const pluginPath = this.pluginInfo.pluginPath;
    for (const command of commands) {
      await execCommandAsync(command, { cwd: pluginPath });
    }
  }

  async packPackage() {
    const pluginOutputPath = this.pluginInfo.pluginFullOutPath;
    const tgzFilePath = this.getPackFilePath(pluginOutputPath);
    fsExtra.removeSync(tgzFilePath);

    const command = 'npm pack';
    await execCommandAsync(command, { cwd: pluginOutputPath });
  }

  async movePackage() {
    const tgzFilePath = this.getPackFilePath(this.pluginInfo.pluginFullOutPath);
    const libFilePath = this.getPackFilePath(this.projectInfo.projectLibPath);
    await fsExtra.copyFile(tgzFilePath, libFilePath);
  }

  async installPackage() {
    const projectLibPath = this.projectInfo.projectLibPath;
    const projectPath = this.projectInfo.projectPath;
    const projectPackFilePath = this.getPackFilePath(projectLibPath);
    const command = `npm i ${projectPackFilePath}`;
    await execCommandAsync(command, { cwd: projectPath });
  }

  /**
   * 获取 npm pack 后的文件路径
   */
  getPackFilePath(outPath) {
    return path.join(outPath, this.pluginInfo.packFileName);
  }
}

function install(argv) {
  return new InstallCommand(argv);
}

module.exports = install;
module.exports.InstallCommand = InstallCommand;
